/**
 * Author: Jackin (qin.zhang@uni.lu)
 * Date: 2013
 * (c) 2013 University of Luxembourg - Interdisciplinary Centre for Security Reliability and Trust (SnT)
 * All rights reserved
 * GPLv3 to be found at: http://www.gnu.org/licenses/quick-guide-gplv3.html
 */

package lu.uni.geko.ext.ram;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;

import lu.uni.geko.resources.MainResourceLoader;
import lu.uni.geko.util.ecore.FeatureEquivalenceHelper;
import lu.uni.geko.weaver.Advice;
import lu.uni.geko.weaver.AdviceEffectuation;
import lu.uni.geko.weaver.HashMappedAdvice;
import lu.uni.geko.weaver.add.MainAdder;
import lu.uni.geko.weaver.merge.Merger;
import lu.uni.geko.weaver.remove.Remover;
import lu.uni.geko.weaver.scope.AdviceAndScopeResolver;

import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.util.EcoreUtil;

/**
 * This class is a wrapped weaver for RAM aspects, which adapts partial functionality of the default GeKo weaver.
 * 
 * @author Jackin
 */
public final class RamWeaver
{
	private HashMap<URI, URI> backup; // backup RAM aspects for final recovery after weaving

	/**
	 * Constructor of the wrapped weaver.
	 */
	public RamWeaver()
	{
		this.backup = new HashMap<URI, URI>(); // Pair <baseURI, wovenURI>
	}

	public final void weave(final URI baseURI)
	{
		// backup the current base aspect into the woven URI
		URI wovenURI = RamUtil.copyBaseToWovenURI(baseURI);
		this.backup.put(baseURI, wovenURI);

		// load current base aspect and its instantiation list
		ram.Aspect base = (ram.Aspect) MainResourceLoader.getUniqueContentRoot(baseURI, "current base model");
		EList<ram.Instantiation> instantiations = base.getInstantiations();

		if (instantiations != null)
		{ // if the current base RAM aspect is still depending on other aspects, then weave its all depending aspects
			for (int instantiationIndex = 0; instantiationIndex < instantiations.size(); instantiationIndex++)
			{ // All the aspects which the current base aspect are depending can be fetched from the instantiations
				try
				{
					// The dependent is an external aspect which the current base aspect is depending on
					ram.Instantiation instantiation = instantiations.get(instantiationIndex);
					ram.Aspect dependent = (ram.Aspect) instantiation.getExternalAspect();
					URI dependentURI = EcoreUtil.getURI(dependent);

					// check if the dependent aspect is loaded
					RamChecker.checkAspect(dependent);

					// If the dependent aspect is OK, go recursively to weave it with all its dependent aspects
					weave(dependentURI);

					/*
					 * When finishing weaving the dependent aspect with all its own dependent aspects, weave this dependent aspect
					 * onto the current base aspect.
					 */
					performWeaving(baseURI, instantiationIndex);

				} catch (Exception ex)
				{
					ex.printStackTrace();
				}
			} // end of for
		} // end of if
		
		// clear instantiation list and layout
		base = (ram.Aspect) MainResourceLoader.getUniqueContentRoot(baseURI, "current base model");
		base.setLayout(null);
		base.getInstantiations().clear();
		MainResourceLoader.save(baseURI);

	}

	private final void performWeaving(URI baseURI, int instantiationIndex)
	{
		// Refresh instantiation and dependent aspect
		ram.Aspect base = (ram.Aspect) MainResourceLoader.getUniqueContentRoot(baseURI, "current base model");
		ram.Instantiation instantiation = base.getInstantiations().get(instantiationIndex);
		ram.Aspect dependent = (ram.Aspect) instantiation.getExternalAspect();
		
		if (instantiation.getType().equals(ram.InstantiationType.DEPENDS))
		{
			RamChecker.checkMandatoryAspectParameters(instantiation, dependent);
			RamUtil.changeVisibility(dependent);
			RamUtil.instantiate(instantiation);
		}
		else
		{
			RamUtil.buildDefaultMappings(base, dependent, instantiation);
		}

		Advice advice = new HashMappedAdvice();
		RamAdviceEffectuationCalculator calculator = new RamAdviceEffectuationCalculator();
		AdviceEffectuation adviceEffectuation = calculator.calculateAdviceEffectuation(base, dependent, instantiation, advice);
		
//		changeVisibility(dependentURI);
//
//
//		
//		if (instantiation.getType().getLiteral().equalsIgnoreCase(RamConstants.INSTANTIATION_TYPE_DEPENDS))
//			instantiate(instantiation, dependentURI);
//
//		Advice advice = AdviceAndScopeResolver.resolveAvAndInstantiationScopes(dependentURI);
//		AdviceEffectuation adviceEffectuation = RamAdviceEffectuationCalculator.calculateAdviceEffectuation(baseURI, instantiationIndex);
//
//		// Adapt original GeKo weaver functionality
//		Merger merger = new Merger(advice, adviceEffectuation, baseURI);
//		FeatureEquivalenceHelper featureEquivalenceHelper = merger.performMergesAndReturnFeatureEquivalenceHelper();
//		MainAdder.performAdditions(advice, adviceEffectuation, featureEquivalenceHelper, baseURI);
//		Remover.performRemovals(adviceEffectuation);
//
//		// Save the woven result into the baseURI. The original base aspect can be recovered after all weaving tasks done,
//		// by calling the "recover" method.
//		MainResourceLoader.save(baseURI);
	}

//	private final void weaveMessageViewWithinAspect(URI baseURI)
//	{
//		ram.Aspect base = (ram.Aspect) MainResourceLoader.getUniqueContentRoot(baseURI, "current base aspect");
//		Iterator<ram.AbstractMessageView> iter = base.getMessageViews().iterator();
//		
//		while (iter.hasNext())
//		{
//			ram.AbstractMessageView abstractMessageView = iter.next();
//			
//			if (abstractMessageView instanceof ram.MessageView)
//			{
//				ram.MessageView mv = (ram.MessageView) abstractMessageView;
//				
//				if (mv.getSpecification() != null && !mv.getAffectedBy().isEmpty())
//				{
//					EList<ram.AspectMessageView> aspectMessageViews = mv.getAffectedBy();
//					
//					for (ram.AspectMessageView amv : aspectMessageViews)
//					{
//						int nextIndex = 1; // ignore the first interaction fragment since it is always the call of the method
//						ram.Interaction advice = amv.getAdvice();
//						advice.getFragments().remove(0);
//								
//					}
//					
//				} // end if
//			} // end if instance of
//		} // end while
//	}

	

	

	public final void recover()
	{
		Iterator<URI> iter = this.backup.keySet().iterator();

		while (iter.hasNext())
		{
			URI baseURI = iter.next();
			URI wovenURI = this.backup.get(baseURI);

			// switch the contents in baseURI and wovenURI
			ram.Aspect base = (ram.Aspect) MainResourceLoader.getUniqueContentRoot(wovenURI, "the base model for recovery");
			ram.Aspect woven = (ram.Aspect) MainResourceLoader.getUniqueContentRoot(baseURI, "the woven model for recovery");
			MainResourceLoader.saveEObjectAsOnlyContent(base, baseURI);
			MainResourceLoader.saveEObjectAsOnlyContent(woven, wovenURI);
		}
	}
}
